package com.guli.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.client.utils.StringUtils;
import com.guli.common.utils.R;
import com.guli.search.service.SearchService;
import com.guli.search.vo.SearchParam;
import com.guli.search.vo.SearchResult;
import com.guli.search.vo.SkuVo;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author WZH
 * @title: SearchServiceImpl
 * @projectName gulimall-java
 * @description:
 * @date 2022/1/7
 */
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private RestHighLevelClient client;


    @Override
    public R addAll(List<SkuVo> collect) {
        BulkRequest request = new BulkRequest("product");

        collect.stream().forEach(item->{
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.id(item.getSkuId().toString());
            String jsonString = JSONObject.toJSONString(item);
            indexRequest.source(jsonString, XContentType.JSON);
            request.add(indexRequest);
        });
        try {
            BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.ok();
    }

    @Override
    public SearchResult gotoSearch(SearchParam param) {
        //构建条件
        SearchRequest searchRequest = new SearchRequest("product");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(param.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }
        if (param.getHasStock()){
            boolQueryBuilder.must(QueryBuilders.termQuery("hasStock",param.getHasStock()));
        }
        if (StringUtils.isNotBlank(param.getSkuPrice())){
            //400_1900 区间     400_ 大于400  _2000 小于2000
            String[] prices = param.getSkuPrice().split("_");
            if (param.getSkuPrice().startsWith("_")){//开头匹配
                boolQueryBuilder.must(QueryBuilders.rangeQuery("skuPrice").lte(prices[1]));
            } else if (param.getSkuPrice().endsWith("_")){//结尾匹配
                boolQueryBuilder.must(QueryBuilders.rangeQuery("skuPrice").gte(prices[0]));
            }else {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("skuPrice").lte(prices[1]).gte(prices[0]));
            }
        }
        if (param.getBrandId()!=null&& param.getBrandId().size()>0){
            boolQueryBuilder.must(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }
        if (param.getCatalogId()!=null&&param.getCatalogId()!=0){
            boolQueryBuilder.must(QueryBuilders.termQuery("catalogId",param.getCatalogId()));
        }

        if (param.getAttrs()!=null&&param.getAttrs().size()>0) {
            //多个非扁平化处理的bool查询
            //attrs=1_3G:4G:5G&
            //attrs=2_骁龙845&
            //attrs=4_高清屏
            param.getAttrs().stream().forEach(item->{
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                String[] key = item.split("_");
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId",key[0]));
                String[] value = key[1].split(":");
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",value));
                NestedQueryBuilder attrs1 = QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None);
                boolQueryBuilder.must(attrs1);
            });
        }
        //聚合处理 brandIdAgg
        TermsAggregationBuilder brandAggregation = AggregationBuilders.terms("brandIdAgg").field("brandId");
        brandAggregation.subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"));
        brandAggregation.subAggregation(AggregationBuilders.terms("brandImgAgg").field("brandImg"));
        searchSourceBuilder.aggregation(brandAggregation);
        // cataIdAgg
        TermsAggregationBuilder cataAggregation = AggregationBuilders.terms("cataIdAgg").field("catalogId");
        cataAggregation.subAggregation(AggregationBuilders.terms("cataName").field("catalogName"));
        searchSourceBuilder.aggregation(cataAggregation);
        //attrAgg
        NestedAggregationBuilder nested = AggregationBuilders.nested("attrAgg", "attrs");
        TermsAggregationBuilder attrAggregation = AggregationBuilders.terms("attrsIdAgg").field("attrs.attrId");
        attrAggregation.subAggregation(AggregationBuilders.terms("attrsNameAgg").field("attrs.attrName"));
        attrAggregation.subAggregation(AggregationBuilders.terms("attrsValueAgg").field("attrs.attrValue"));
        nested.subAggregation(attrAggregation);
        searchSourceBuilder.aggregation(nested);

        searchSourceBuilder.sort("saleCount", SortOrder.DESC);
        searchSourceBuilder.from((param.getPageNum()-1)*param.getSize());
        searchSourceBuilder.size(param.getSize());
        searchSourceBuilder.highlighter(new HighlightBuilder().field("skuTitle")
                .preTags("<span style='color:red'>").postTags("</span>"));
        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);

        //同步执行
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

            //返回结果
            SearchHits hits = searchResponse.getHits();
            TotalHits totalHits = hits.getTotalHits();
// the total number of hits, must be interpreted in the context of totalHits.relation
            long numHits = totalHits.value;
// whether the number of hits is accurate (EQUAL_TO) or a lower bound of the total (GREATER_THAN_OR_EQUAL_TO)
            TotalHits.Relation relation = totalHits.relation;
            float maxScore = hits.getMaxScore();
            SearchHit[] searchHits = hits.getHits();
            ArrayList<SkuVo> skuVos = new ArrayList<>();
            for (SearchHit hit : searchHits) {
                // do something with the SearchHit
                String index = hit.getIndex();
                String id = hit.getId();
                float score = hit.getScore();
                String sourceAsString = hit.getSourceAsString();
                SkuVo skuVo = JSONObject.parseObject(sourceAsString, SkuVo.class);
                skuVo.setSkuId(Long.parseLong(id));
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlight = highlightFields.get("skuTitle");
                if (highlight!=null){
                    Text[] fragments = highlight.fragments();
                    String fragmentString = fragments[0].string();
                    skuVo.setSkuTitle(fragmentString);
                }
                skuVos.add(skuVo);
            }
            //聚合处理
            Aggregations aggregations = searchResponse.getAggregations();
            //brandIdAgg
            Terms brandIdAgg = aggregations.get("brandIdAgg");
            List<SearchResult.BrandVo> brandVos = brandIdAgg.getBuckets().stream().map(item -> {
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
                brandVo.setBrandId(item.getKeyAsNumber().longValue());
                Aggregations brandVogAgg = item.getAggregations();
                Terms brandImgAgg = brandVogAgg.get("brandImgAgg");
                brandVo.setBrandImg(brandImgAgg.getBuckets().get(0).getKeyAsString());
                Terms brandNameAgg = brandVogAgg.get("brandNameAgg");
                brandVo.setBrandName(brandNameAgg.getBuckets().get(0).getKeyAsString());
                return brandVo;
            }).collect(Collectors.toList());
            // cataIdAgg
            Terms cataIdAgg = aggregations.get("cataIdAgg");
            List<SearchResult.CatalogVo> catalogVos = cataIdAgg.getBuckets().stream().map(item -> {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                catalogVo.setCatalogId(item.getKeyAsNumber().longValue());
                Aggregations cataaggregations = item.getAggregations();
                Terms cataName = cataaggregations.get("cataName");
                catalogVo.setCatalogName(cataName.getBuckets().get(0).getKeyAsString());
                return catalogVo;
            }).collect(Collectors.toList());
            //attrAgg  !!!查询用什么类型 处理结果就用什么类型
            Nested attrAgg = aggregations.get("attrAgg");
            Aggregations attrNested = attrAgg.getAggregations();
            Terms attrsIdAgg = attrNested.get("attrsIdAgg");
            List<SearchResult.AttrVo> attrVos = attrsIdAgg.getBuckets().stream().map(item -> {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                attrVo.setAttrId(item.getKeyAsNumber().longValue());
                Aggregations attrAggregations = item.getAggregations();
                Terms attrsNameAgg = attrAggregations.get("attrsNameAgg");
                attrVo.setAttrName(attrsNameAgg.getBuckets().get(0).getKeyAsString());
                Terms attrsValueAgg = attrAggregations.get("attrsValueAgg");
                List<String> strings = attrsValueAgg.getBuckets().stream().map(item2 -> {
                    return item2.getKeyAsString();
                }).collect(Collectors.toList());
                attrVo.setAttrValue(strings);
                return attrVo;
            }).collect(Collectors.toList());

            SearchResult searchResult = new SearchResult();
            searchResult.setProducts(skuVos);
            searchResult.setBrands(brandVos);
            searchResult.setCatalogs(catalogVos);
            searchResult.setAttrs(attrVos);
            searchResult.setTotal(numHits);
            searchResult.setPageNum(param.getPageNum());
            searchResult.setPageSize(param.getSize());
            searchResult.setTotalPages(Math.toIntExact(numHits % param.getSize() == 0 ? numHits / param.getSize() : numHits / param.getSize() + 1));
            List<Integer> navs = new ArrayList<>();
            for (int i = 1; i <= searchResult.getTotalPages(); i++) {
                navs.add(i);
            }
            searchResult.setPageNavs(navs);
            return searchResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
